package ags.muse.gun;

import java.awt.Polygon;
import java.util.List;
import java.util.ArrayList;

/**
 * Represents a hypothetical aim profile with discrete increases and decreases
 * 
 * @author Alexander Schultz
 */
public class RadialAimProfile {
    private final List<double[]> data = new ArrayList<double[]>();
    private double zeroValue = 0;

    /**
     * Add a guessfactor range with a certain weight
     */
    public void add(double angle, double width, double weight) {
        double halfWidth = width * 0.5;
        double lower = robocode.util.Utils.normalAbsoluteAngle(angle - halfWidth);
        double upper = robocode.util.Utils.normalAbsoluteAngle(angle + halfWidth);
        sortedEntryInsert(lower, weight);
        sortedEntryInsert(upper, -weight);

        if (lower > upper) {
            zeroValue += weight;
        }
    }

    /**
     * Do a sorted insert in the list
     */
    private void sortedEntryInsert(double angle, double change) {
        for (int i = 0; i < data.size(); i++) {
            if (data.get(i)[0] > angle) {
                data.add(i, new double[]{angle, change});
                return;
            }
        }
        data.add(new double[]{angle, change});
    }

    /**
     * Normalize the profile to peak at 1
     */
    private double getMaxValue() {
        double value = zeroValue;
        double maxValue = zeroValue;
        for (int i = 0; i < data.size(); i++) {
            value += data.get(i)[1];
            if (value > maxValue) {
                maxValue = value;
            }
        }

        return maxValue;
    }

    /**
     * Returns the GuessFactor at the center of the peak region
     */
    public double getBestAngle() {
        if (data.size() == 0)
            return 0;

        int maxIndex = 0;
        double maxValue = 0;
        double value = zeroValue;
        for (int i = 0; i < data.size() - 1; i++) {
            value += data.get(i)[1];
            if (data.get(i)[0] == data.get(i+1)[0]) {
                continue;
            }
            if (value > maxValue) {
                maxValue = value;
                maxIndex = i;
            }
        }

        // If around the center is the best place to aim
        if (zeroValue > maxValue) {
            double below = 2*Math.PI - data.get(data.size() - 1)[0];
            double above = data.get(0)[0];
            return robocode.util.Utils.normalAbsoluteAngle((above + below)/2);
        }

        return (data.get(maxIndex)[0] + data.get(maxIndex+1)[0])/2;
    }

    public Polygon getGraphPolygon(double x, double y, double radius) {
        final Polygon poly = new Polygon();
        double multiplier = radius / getMaxValue();

        double value = zeroValue;
        for (double[] point : data) {
            double magnitude = value * multiplier;
            double px = x + magnitude*Math.sin(point[0]);
            double py = y + magnitude*Math.cos(point[0]);
            poly.addPoint((int)(px+0.5), (int)(py+0.5));
            value += point[1];
            magnitude = value * multiplier;
            px = x + magnitude*Math.sin(point[0]);
            py = y + magnitude*Math.cos(point[0]);
            poly.addPoint((int)(px+0.5), (int)(py+0.5));
        }

        return poly;
    }
}
